package net.sf.rose.business.bean.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.sf.rose.business.bean.database.FieldMetaBean;
import net.sf.rose.business.bean.database.TableMetaBean;
import net.sf.rose.business.service.ActionService;
import net.sf.rose.business.service.DataBaseService;
import net.sf.rose.exception.SystemErrorCode;
import net.sf.rose.exception.SystemException;
import net.sf.rose.jdbc.DBUtils;
import net.sf.rose.jdbc.DataParser;
import net.sf.rose.jdbc.GenericDAO;
import net.sf.rose.jdbc.QueryMapSQL;
import net.sf.rose.jdbc.service.DataBaseConfigService;
import net.sf.rose.jdbc.service.Service;

import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.apache.commons.jexl2.Script;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Default;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;

/**
 * @author fengjian
 * @since 2014-3-27
 */
@Default
@Root(name = "actionUpdate")
public class ActionUnitUpdateBean extends Action {

	/** 数据库执行操作的类型 */
	@Attribute
	private String type = null;

	/** 数据库表名 */
	@Attribute
	private String table = null;

	/** 要重新执行的执行单元 */
	@ElementList(required = false)
	private List<String> refresh = null;

	/** 要重新执行的执行单元 */
	@Element(required = false)
	private String rules = null;

	/** 要重新执行的执行单元 */
	@Attribute(required = false)
	private String ruleDescription = null;

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public String getTable() {
		return table;
	}

	public void setTable(String table) {
		this.table = table;
	}

	/**
	 * 返回表的定义类
	 */
	public TableMetaBean getTableBean() {
		return DataBaseService.getService().getAll().get(this.table);
	}

	public List<String> getRefresh() {
		return refresh;
	}

	public void setRefresh(List<String> refresh) {
		this.refresh = refresh;
	}

	public String getRules() {
		return rules;
	}

	public void setRules(String rules) {
		this.rules = rules;
	}

	public String getRuleDescription() {
		return ruleDescription;
	}

	public void setRuleDescription(String ruleDescription) {
		this.ruleDescription = ruleDescription;
	}

	/**
	 * 执行自定义操作
	 * @param request 	- Http请求
	 * @param response  - Http返回
	 * @param service 	- 数据库连接服务类 
	 * @param map 		- 前台传过来的数据
	 * @param data		- 上下文返回的数据
	 */
	public void execute(HttpServletRequest request, HttpServletResponse response, 
			Service service, Map<String, Object> map, Map<String, Object> data) {
		String pk = null;
		TableMetaBean tableBean = this.getTableBean();
		FieldMetaBean fieldBean = tableBean.getPKFieldBean();
		if (fieldBean != null) {
			pk = fieldBean.getName();
		}
		GenericDAO dao = new GenericDAO(service);
		QueryMapSQL query = new QueryMapSQL(service);
		if (type.equals(DBUtils.TYPE_UPDATE)) {
			Map<String, net.sf.rose.jdbc.bean.FieldBean> fields = DBUtils.getFields(table);
			Map<String, Object> m = parseTableData(map,tableBean.getName(), fields);
			// 如果规则存在，则先将规则执行一遍
			if (this.getRules() != null) {
				JexlEngine JEXL = null;
				JEXL = new JexlEngine();
				JEXL.setCache(512);
				JexlContext jc;
				map.put("map", map);
				jc = new MapContext(map);
				Script s = JEXL.createScript(this.getRules());
				s.execute(jc);
			}
			// 如果没有主键生成策略，则查询数据库来判断此次数据库操作是插入还是更新
			if (tableBean.getKeyGenerate() == null) {
				// 如果主键不存在，则数据库作失败
				if (m.get(pk) == null) {
					String message = tableBean.getText() + '(' + tableBean.getName() + ')' + "保存数据时没有主键值，也没有定义主键策略。";
					throw new SystemException(message, SystemErrorCode.D_SQL_UPDATE_CREATE_ERROR);
				}
				// 如果主键存在
				else {
					// 根据主键查找数据库
					m.put(QueryMapSQL.DEF_SYS_TABLE, this.table);
					query.createLoadSql(m);
					Object db = dao.load(query);
					// 如果有数据，则更新
					if (db != null) {
						query.createUpdateSql(this.table, map);
					}
					// 如果没有数据则新增
					else {
						query.createInsertSql(this.table, map);
					}
				}
			}
			// 如果有主键生成策略，则根据主键来判断此次数据库操作是插入还是更新
			else {
				// 如果主键不存在，则对数据库作插入操作
				if (m.get(pk) != null) {
					m.put(pk, tableBean.getUID());
					query.createInsertSql(this.table, m);
				}
				// 如果主键存在，则对数据库作更新操作
				else {
					query.createUpdateSql(this.table, m);
				}
			}
		} else if (type.equals(DBUtils.TYPE_DELETE)) {
			Object id = map.get(pk);
			if (id != null) {
				Map<String, Object> m = new HashMap<String, Object>();
				m.put(DataBaseConfigService.getDefTableIdName(), id);
				query.createDeleteSql(this.table, m);
			}
		}
		dao.update(query);
		// 如果此更新执行成功后，有需要同步执行的执行单元，则执行它们。
		if (this.getRefresh() != null && this.getRefresh().size() > 0) {
			for (String str : this.getRefresh()) {
				Action unit = ActionService.getAllActionUnit().get(str);
				if (unit != null && (unit instanceof ActionUnitSearchBean)) {
					ActionUnitSearchBean search = (ActionUnitSearchBean) unit;
					search.search(request, response, service, map, data);
				}
			}
		}
	}

	/**
	 * 根据数据库定义的数据格式格式化数据
	 * @param map       - 数据集合
	 * @param tableName - 数据库表名
	 * @param fields    - 数据定义类集合
	 * @return map      - 格式化后的数据集合
	 */
	private Map<String, Object> parseTableData(Map<String, Object> map, String tableName, Map<String, net.sf.rose.jdbc.bean.FieldBean> fields) {
		String key = null;
		Object val = null;
		DataParser paser = new DataParser();
		Map<String, Object> m = new HashMap<String, Object>();
		for (net.sf.rose.jdbc.bean.FieldBean field : fields.values()) {
			key = field.getField();
			val = map.get(key);
			// 如果根据字段名取不取数据
			if (val == null) {
				// 根据表名加字段名取数据，例：user.username
				val = map.get(tableName + '.' + key);
			}
			if (val != null) {
				m.put(key, paser.parse(field.getJavaType(), val, null));
			}
		}
		return m;
	}

}
